/*
 * Copyright (c) 2008 VMware, Inc.
 * Copyright (c) 2009 John Pritchard, WTKX Project Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wtkx.wtk;

import wtkx.Component;
import wtkx.Border;
import wtkx.CardPane;
import wtkx.FlowPane;
import wtkx.ImageView;
import wtkx.Label;
import wtkx.Popup;
import wtkx.ScrollPane;
import wtkx.Separator;
import wtkx.StackPane;
import wtkx.TablePane;
import wtkx.TextArea;
import wtkx.Window;
import wtkx.collections.ArrayList;
import wtkx.collections.HashMap;
import wtkx.wtk.media.Image;
import wtkx.wtk.skin.BorderSkin;
import wtkx.wtk.skin.CardPaneSkin;
import wtkx.wtk.skin.FlowPaneSkin;
import wtkx.wtk.skin.ImageViewSkin;
import wtkx.wtk.skin.LabelSkin;
import wtkx.wtk.skin.PopupSkin;
import wtkx.wtk.skin.ScrollPaneSkin;
import wtkx.wtk.skin.SeparatorSkin;
import wtkx.wtk.skin.StackPaneSkin;
import wtkx.wtk.skin.TablePaneSkin;
import wtkx.wtk.skin.TextAreaSkin;
import wtkx.wtk.skin.WindowSkin;

import java.awt.Font;
import java.lang.reflect.Modifier;


/**
 * Base class for Wtkx themes. A theme defines a complete "look and feel"
 * for a Wtkx application.
 * <p>
 * Note that concrete Theme implementations should be declared as final. If
 * multiple third-party libraries attempted to extend a theme, it would cause a
 * conflict, as only one could be used in any given application.
 * <p>
 * IMPORTANT All skin mappings must be added to the map, even non-static inner
 * classes. Otherwise, the component's base class will attempt to install its
 * own skin, which will result in the addition of duplicate listeners.
 */
public abstract class Theme 
    extends Object
{

    private static Theme theme = null;

    public static Theme getTheme() {
        if (null == theme)
            throw new IllegalStateException("Missing theme.");
        else
            return theme;
    }
    public static void setTheme(Theme theme) {
        if (null == theme)
            throw new IllegalArgumentException();
        else {
            Theme previousTheme = Theme.theme;
            if (previousTheme != null) {
                previousTheme.uninstall();
            }

            theme.install();
            Theme.theme = theme;

            if (previousTheme != null) {
                Component.ComponentDictionary components = Component.getComponents();
                ArrayList<Integer> componentHandles = new ArrayList<Integer>();

                for (Integer handle : components) {
                    componentHandles.add(handle);
                }

                for (Integer handle : componentHandles) {
                    Component component = components.get(handle);
                    Class<? extends Component> componentClass =
                        (Class<? extends Component>)component.getClass();

                    if (theme.componentSkinMap.containsKey(componentClass)
                        && (componentClass.getEnclosingClass() == null
                            || (componentClass.getModifiers() & Modifier.STATIC) == Modifier.STATIC)) {
                        component.installSkin(componentClass);
                    }
                }
            }
        }
    }


    protected HashMap<Class<? extends Component>, Class<? extends Skin>> componentSkinMap =
        new HashMap<Class<? extends Component>, Class<? extends Skin>>();


    public Theme() {
        super();
        componentSkinMap.put(Border.class, BorderSkin.class);
        componentSkinMap.put(CardPane.class, CardPaneSkin.class);
        componentSkinMap.put(FlowPane.class, FlowPaneSkin.class);
        componentSkinMap.put(ImageView.class, ImageViewSkin.class);
        componentSkinMap.put(Label.class, LabelSkin.class);
        componentSkinMap.put(Popup.class, PopupSkin.class);
        componentSkinMap.put(ScrollPane.class, ScrollPaneSkin.class);
        componentSkinMap.put(Separator.class, SeparatorSkin.class);
        componentSkinMap.put(StackPane.class, StackPaneSkin.class);
        componentSkinMap.put(TablePane.class, TablePaneSkin.class);
        componentSkinMap.put(TextArea.class, TextAreaSkin.class);
        componentSkinMap.put(Window.class, WindowSkin.class);
    }


    public final Class<? extends Skin> getSkinClass(Class<? extends Component> componentClass) {
        return this.componentSkinMap.get(componentClass);
    }

    public abstract void install();
    public abstract void uninstall();

    public abstract Font getFont();
    public abstract Image getMessageIcon(MessageType messageType);
    public abstract Image getSmallMessageIcon(MessageType messageType);

}
